
#include "nane/InputSystem/InputSystem.h"
#include "nane/InputSystem/InputSystemImpl_.h"
#include "nane/core/assert.h"
#include "nane/core/log.h"
#include "nane/core/crt/ModuleManager.h"
#include "nane/core/function/Functor.h"

namespace nane
{
    ////////////////////////////////////////////////////////////////////////////////////////////////
    InputSystem::InputSystem()
        : m( NULL )
    {
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    InputSystem::~InputSystem()
    {
        Shutdown();
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool InputSystem::Initialize( IWindow::OSHandle _windowHandle )
    {
        if( m != NULL )
        {
            if( m->windowHandle != _windowHandle )
            {
                LOG_ERROR("InputSystem already initialized for different IWindow. You must shutdown it first");
                return false;
            }
            else
            {
                return true;
            }
        }
        m = new Implementation();
        m->windowHandle = _windowHandle;
        ModuleManager::ForeachModule("IInputDeviceSystem", MakeMemberFunctor(&Implementation::LoadModule, m));
        for( Implementation::TInputDeviceSystemVector::iterator it = m->inputDevices.begin(), it_end = m->inputDevices.end();
                it != it_end;
                ++it )
        {
            if( (*it)->InitializeContext(_windowHandle) == true )
            {
                m->inputDevicesWithContext.push_back(*it);
            }
        }        
        return true;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void InputSystem::Shutdown()
    {
        if( m != NULL )
        {
            for( Implementation::TInputDeviceSystemVector::reverse_iterator it = m->inputDevicesWithContext.rbegin(), it_end = m->inputDevicesWithContext.rend();
                    it != it_end;
                    ++it )
            {            
                (*it)->ShutdownContext();
            }
            delete m;
            m = NULL;
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool InputSystem::AddInputDevice( InputDeviceSystem* _inputDeviceSystem )
    {
        NANE_ASSERT_RV( m != NULL, "InputSystem must be initialized", false );
        if( _inputDeviceSystem->InitializeContext(m->windowHandle) == true )
        {
            m->inputDevicesWithContext.push_back(_inputDeviceSystem);
            return true;
        }
        return false;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void InputSystem::RemoveInputDevice( InputDeviceSystem* _inputDeviceSystem )
    {
        NANE_ASSERT_R( m != NULL, "InputSystem must be initialized" );
        Implementation::TInputDeviceSystemVector::iterator it_end = 
                std::remove_if(m->inputDevicesWithContext.begin(), m->inputDevicesWithContext.end(), Implementation::FindDevice(_inputDeviceSystem));
        m->inputDevicesWithContext.erase(it_end);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void InputSystem::Update()
    {
        NANE_ASSERT_R( m != NULL, "InputSystem must be initialized" );

        for( Implementation::TInputActionVector::iterator it = m->inputActions.begin(), it_end = m->inputActions.end();
                it != it_end;
                ++it )
        {
            it->second.PreUpdate();
        }

        for( Implementation::TInputDeviceSystemVector::iterator it = m->inputDevices.begin(), it_end = m->inputDevices.end();
                it != it_end;
                ++it )
        {
            InputDeviceSystem* inputDevice = (*it);
            EInputDevice deviceType = inputDevice->GetDeviceType();
            InputEvent* inputEvent = NULL;
            while( (inputEvent = inputDevice->Poll()) != NULL )
            {
                for( Implementation::TInputActionVector::iterator it = m->inputActions.begin(), it_end = m->inputActions.end();
                        it != it_end;
                        ++it )
                {
                    it->second.Update(deviceType, inputEvent);
                }
            }
        }
        
        for( Implementation::TInputActionVector::iterator it = m->inputActions.begin(), it_end = m->inputActions.end();
                it != it_end;
                ++it )
        {
            it->second.PostUpdate();
        }
        
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void InputSystem::AddActionBinding( const char* _action, const InputBinding& _inputBinding )
    {
        NANE_ASSERT_R( m != NULL, "InputSystem must be initialized" );
        m->AddActionBinding(_action, _inputBinding, 0, NULL);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void InputSystem::AddActionBinding( const char* _action, const InputBinding& _inputBinding, const InputBindingModifier& _modifier )
    {
        NANE_ASSERT_R( m != NULL, "InputSystem must be initialized" );
        m->AddActionBinding(_action, _inputBinding, 1, &_modifier);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void InputSystem::AddActionBinding( const char* _action, const InputBinding& _inputBinding, const InputBindingModifier& _modifier1, const InputBindingModifier& _modifier2 )
    {
        NANE_ASSERT_R( m != NULL, "InputSystem must be initialized" );
        const InputBindingModifier modifiers[] = { _modifier1, _modifier2 };
        m->AddActionBinding(_action, _inputBinding, 2, modifiers);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void InputSystem::RemoveActionBinding( const char* _action, const InputBinding& _inputBinding )
    {
        NANE_ASSERT_R( m != NULL, "InputSystem must be initialized" );
        Implementation::TInputActionVector::iterator it_find = 
                std::find_if(m->inputActions.begin(), m->inputActions.end(), Implementation::FindAction(_action));
        if( it_find != m->inputActions.end() )
        {
            it_find->second.RemoveInputBinding(_inputBinding);
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    float InputSystem::GetActionValue( const char* _action )
    {
        NANE_ASSERT_RV( m != NULL, "InputSystem must be initialized", 0.0f );
        float value = 0.0f;
        Implementation::TInputActionVector::iterator it_find = 
                std::find_if(m->inputActions.begin(), m->inputActions.end(), Implementation::FindAction(_action));
        if( it_find != m->inputActions.end() )
        {
            value = it_find->second.GetValueLast();
        }
        return value;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool InputSystem::GetActionIsPressed( const char* _action )
    {
        NANE_ASSERT_RV( m != NULL, "InputSystem must be initialized", false );
        bool isPressed = false;
        Implementation::TInputActionVector::iterator it_find = 
                std::find_if(m->inputActions.begin(), m->inputActions.end(), Implementation::FindAction(_action));
        if( it_find != m->inputActions.end() )
        {
            isPressed = it_find->second.IsPressed();
        }
        return isPressed;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    uint32 InputSystem::GetActionWasPressed( const char* _action )
    {
        NANE_ASSERT_RV( m != NULL, "InputSystem must be initialized", 0 );
        uint32 wasPressed = 0;
        Implementation::TInputActionVector::iterator it_find = 
                std::find_if(m->inputActions.begin(), m->inputActions.end(), Implementation::FindAction(_action));
        if( it_find != m->inputActions.end() )
        {
            wasPressed = it_find->second.WasPressed();
        }
        return wasPressed;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool InputSystem::GetActionIsReleased( const char* _action )
    {
        NANE_ASSERT_RV( m != NULL, "InputSystem must be initialized", false );
        bool isReleased = true;
        Implementation::TInputActionVector::iterator it_find = 
                std::find_if(m->inputActions.begin(), m->inputActions.end(), Implementation::FindAction(_action));
        if( it_find != m->inputActions.end() )
        {
            isReleased = it_find->second.IsReleased();
        }
        return isReleased;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    uint32 InputSystem::GetActionWasReleased( const char* _action )
    {
        NANE_ASSERT_RV( m != NULL, "InputSystem must be initialized", 0 );
        uint32 wasReleased = 0;
        Implementation::TInputActionVector::iterator it_find = 
                std::find_if(m->inputActions.begin(), m->inputActions.end(), Implementation::FindAction(_action));
        if( it_find != m->inputActions.end() )
        {
            wasReleased = it_find->second.WasReleased();
        }
        return wasReleased;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
}   // namespace nane
